import React from 'react';
import './App.css';
import { Parser } from './compiler/Parser';
import Layout from 'antd/lib/layout';
import Sider from 'antd/lib/layout/Sider';
import Tabs from 'antd/lib/tabs';
import { Token } from './compiler/common/Token';
import { Button, Divider, PageHeader, Result, Spin, Typography } from 'antd';
import Title from 'antd/lib/typography/Title';
import Paragraph from 'antd/lib/typography/Paragraph';
import { Controlled as CodeMirror } from 'react-codemirror2'
import { DFA, PL0SemanticError, PL0SyntaxError, TokenType } from './compiler/common/Constrants';
import { EditorChange } from 'codemirror';
import TextArea from 'antd/lib/input/TextArea';
import ReactECharts from 'echarts-for-react';
require('codemirror/mode/wast/wast');
require('./modes/pl0');

const { TabPane } = Tabs;
interface State {
    code: string,
    ready: boolean,
    tokens: Token[],
    watCode: string[],
    error: string,
    input: string,
    output: number[] | null

}
export default class App extends React.Component<{}, State> {
    options = DFA.toEchartsGraph()
    constructor(props: {}) {
        super(props);
        const code = `const z = 4;
var a, b;
begin
    read(a, b);
    b := a + b - z;
    write(b)
end.
`
        this.state = {
            code: code,
            ready: false,
            tokens: [],
            watCode: [],
            error: '',
            input: '',
            output: null
        }
        this.updateCode = this.updateCode.bind(this)
        this.compile = this.compile.bind(this)
        this.run = this.run.bind(this)
    }
    parser: Parser = null!
    componentDidMount() {
        this.parser = new Parser()
        this.parser.init().then(() => {
            this.setState({ ready: true })
        })
        console.log(this.options);
        
    }

    render() {
        let layout = <Layout style={{ textAlign: 'center' }}><Spin style={{ margin: 'auto' }} /></Layout>
        if (this.state.error) {
            layout = (<Typography>
                <Title level={3}>编译阶段发生错误</Title>
                <Paragraph>
                    {this.state.error}
                </Paragraph>
                <Paragraph>
                    请检查代码中的错误，然后重新编译
                </Paragraph>
            </Typography>)
        } else if (this.state.ready) {
            let tokens, watCodes, run
            tokens = watCodes = run = (<Result title='请先编译代码' extra={
                <Button type="primary" key="console" onClick={this.compile}>
                    编译代码
                </Button>
            } />)
            // 编译后Tokens
            if (this.state.tokens.length > 0) {
                let trs = this.state.tokens.map((token, i) => {
                    return (<tr key={i}>
                        <td>{TokenType[token.type]}</td>
                        <td>{token.value}</td>
                    </tr>)
                })
                tokens = (
                    <table style = {{width: '100%'}}>
                        <tbody className='tb'> 
                            <tr key={-1}>
                                <th>类型</th>
                                <th>值</th>
                            </tr>
                            {trs} 
                        </tbody>
                    </table>
                )
                // 编译后WatCodes
                if (this.state.watCode.length > 0) {
                    watCodes = (<CodeMirror ref="wat"
                        value={this.state.watCode.join('\n')}
                        options={{
                            lineNumbers: true,
                            readOnly: true,
                            mode: 'wast'
                        }}
                        onBeforeChange={() => {}}
                    />)
                    let outputStr = '请先点击运行代码'
                    if (this.state.output) {
                        outputStr = this.state.output.length ? this.state.output.join(' ') : '无输出结果'
                    } 
                    run = (                    
                        <Typography>
                            <Title level={3}>输入</Title>
                            <Paragraph>
                                注意需要输入程序运行所读取的全部变量，未输入变量会当作0处理。
                                <ul>
                                    <li>程序最多只能读入/输出63个变量</li>
                                    <li>其它字符为分隔符</li>
                                    <li>目前只支持整数</li>
                                </ul>
                            </Paragraph>
                            <TextArea
                                value={this.state.input}
                                onChange={(e) => {
                                    this.setState({ input: e.currentTarget.value })
                                }}
                                placeholder="请输入读入的变量值"
                                autoSize={{ minRows: 1, maxRows: 5 }}
                                key='input'
                            />
                            <Divider key = 'd1'/>,
                            <Button onClick={this.run} key='run'>
                                运行
                            </Button>
                            <Divider key = 'd2'/>
                            <Title level={3}>输出结果</Title>
                            <Paragraph>
                                {outputStr}
                            </Paragraph>
                        </Typography>
                    )
                    
                }
            }
            // 编译后run
            layout = (
                <Tabs tabPosition='right' style={{height: '100%'}}>
                    <TabPane tab="DFA" key="DFA">
                        <ReactECharts option={this.options} style={{height: '80%'}}/>
                    </TabPane>
                    <TabPane tab="Tokens" key="Tokens">{tokens}</TabPane>
                    <TabPane tab="Wat Code" key="Wat Code">
                        {watCodes}
                    </TabPane>
                    <TabPane tab="Run" key="Run">{run}</TabPane>
                </Tabs>
            )
        }
        let options = {
            theme: 'material',
            lineNumbers: true,
            readOnly: false,
            mode: 'pl0'
            // mode: this.state.mode
        };
        return (
            <Layout style={{ width: '100%', height: '100%' }}>
                <Sider width='50%'>
                    <CodeMirror ref="editor"
                        value={this.state.code}

                        onBeforeChange={(editor, data, code) => {
                            this.setState({code: code})
                        }}
                        options={options}
                    />
                </Sider>
                <Layout>
                    <PageHeader
                        className="site-page-header"
                        title="pl0-wasm-compiler"
                        subTitle="By FurtherBank"
                        extra={[
                            <Button key="compile" onClick={this.compile}>编译代码</Button>
                        ]}
                    />
                    {layout}
                </Layout>
            </Layout>
        );
    }

    updateCode(editor: any, data: EditorChange, code: string) {
        console.log(editor, data, code);

        this.setState({
            code: code
        });
    }

    compile() {
        try {
            let tokens = this.parser.analyze(this.state.code)
            let watCode = this.parser.parse()!
            this.setState({
                error: '',
                tokens: tokens,
                watCode: watCode,
                input: '',
                output: null
            })
        } catch (error) {
            console.warn(error);
            if (error instanceof PL0SyntaxError) {
                this.setState({ error: '语法错误：' + error.message })
            } else if (error instanceof PL0SemanticError) {
                this.setState({ error: '语义错误：' + error.message })
            } else {
                this.setState({ error: '意外的错误' })
            }
        }
    }

    run() {
        let inputs = this.state.input.match(/[-]?[\d]+/g)?.map((value) => {
            return parseInt(value)
        })
        // 输入长度不能超过63字符
        if (inputs && inputs.length > 63) {
            inputs.splice(63, inputs.length)
        }
        console.log(inputs);
        try {
            const output = this.parser.compile(inputs ? inputs : [])
            this.setState({ output: output })
        } catch (error) {
            let e = error as Error
            console.warn(error);
            this.setState({ error: '运行时出错：' + e.message })
            
        }
    }
}
